<?php
namespace GrouppaGroup;
require_once 'MessageFromApp.php';
require_once 'MessageFromServer.php';
require_once 'ForumTopic.php';
require_once 'ForumComment.php';
require_once 'Controller.php';

class ForumController extends Controller
{

    /**
     * receives a message from an application (override from controller)
     * @param  MessageFromApp    $message
     * @return MessageFromServer return message to the user
     */
    public function receiveMessage(MessageFromApp $message)
    {

        //CHECK: check access token
        if (!$this->checkAccess($message))
            return;
        //CHECK: handle received message
        if ($message->task) {
            switch ($message->task) {
                case 'getAllTopics':
                    $this->getAllTopics($message);
                    break;
                case 'getAllComments':
                    $this->getAllComments($message);
                    break;
                case 'addComment':
                    $this->addComment($message);
                    break;
                case 'addTopic':
                    $this->addTopic($message);
                    break;
                case 'changeTopic':
                    $this->changeTopic($message);
                    break;
                default:
                    self::unknownTask($message);
                    break;
            }
        }
    }

    /*
    ****************** Section: functions available to call for the applications
    */

    /**
     * gets all topics from forum of current group
     * @return array array of topics, or NULL if none exist
     */
    /**
     * gets all topics from forum of current group, may be restricted with since time
     * WARNING: will not return any response if there are no topics found
     * @param  MessageFromApp $message a message with arguments:
     *                                 groupName, sinceVersion (optional, inclusive version since which load all topics)
     * @throws \Exception     iff there is a data error or database error
     * @return int            number of topics found
     */
    public function getAllTopics(MessageFromApp $message)
    {
        //CHECK: retrieve and return all topics for current active group
        if (!$message->serializedArguments || !is_a($message->serializedArguments,
            'stdClass')) {
            throw new \Exception('ForumController::getAllTopics error: message didn\'t contain proper json object');
        }
        $args = $message->serializedArguments;
        if (!property_exists($args, 'groupName')) {
            throw new \Exception('ForumController::getAllTopics error: message parameters didn\'t contain group name');
        }

        $groupName = $args->groupName;
        $versionFilter = '';
        if (property_exists($args, 'sinceVersion') && is_numeric($args->sinceVersion)) {
            $versionFilter = ' AND forum_topics.version > ' . $args->sinceVersion;
        }

        $db = Db::getInstance();

        //getting author id and group id, checking topic id
        $result = $db->sendQuery('SELECT forum_topics.id_topic , users.user_name, is_pinned, forum_topics.text, forum_topics.title ,forum_topics.date_added, COUNT(forum_comments.id_comment) ,forum_topics.version' .
            ' FROM users, groups, forum_topics LEFT JOIN forum_comments ON forum_topics.id_topic = forum_comments.id_topic' .
            ' WHERE users.id_user = forum_topics.id_author ' . $versionFilter .
            ' AND forum_topics.id_group = groups.id_group AND groups.group_name="' . $groupName .
            '" GROUP BY forum_topics.id_topic;');

        $topics = array();
        $n = $result->num_rows;
        if ($n == 0)
            return 0;
        $version = 0;
        for ($i = 0; $i < $n; $i++) {
            $row = $result->fetch_row();
            $topics[] = array(
                'author' => $row[1],
                'id' => $row[0],
                'text' => $row[3],
                'isPinned' => ($row[2]) ? true : false,
                'title' => $row[4],
                'date' => $row[5],
                'commentsAmount' => $row[6]);
            $version = ($version < $row[7]) ? $row[7] : $version;
        }

        $serializedArguments = json_encode(array(
            'succeeded' => true,
            'topics' => $topics,
            'groupName' => $groupName,
            'maxVersion' => $version));

        $responseMessage = new MessageFromServer($serializedArguments, 'Forum', $message->
            messageType, $message->id);
        $responseMessage->toController = 'WallView';
        ApplicationTCPConnection::sendMessage($responseMessage);

        return $n;
    }

    /**
     * gets all comments from forum of current group and provided topic
     * can be restricted to getting comments since specified time
     * @param  MessageFromApp $message message with parameters:
     *                                 groupName, topicId, sinceID (optional)
     * @throws \Exception     iff something was wrong with data or connection
     * @return int            number of comments retreived
     */
    public function getAllComments(MessageFromApp $message)
    {
        //CHECK: retrieve and return all topics for current active group
        if (!$message->serializedArguments || !is_a($message->serializedArguments,
            'stdClass')) {
            throw new \Exception('ForumController::getAllComments error: message didn\'t contain proper json object');
        }
        $args = $message->serializedArguments;
        if (!property_exists($args, 'topicId')) {
            throw new \Exception('ForumController::getAllComments error: message parameters didn\'t contain ' .
                'topic id');
        }

        $topicId = $args->topicId;
        if (!is_numeric($topicId)) {
            throw new \Exception('ForumController::getAllComments error: topic id is not a number');
        }
        $idFilter = '';
        if (property_exists($args, 'sinceID') && is_numeric($args->sinceID)) {
            $idFilter = ' AND forum_comments.id_comment > ' . $args->sinceID;
        }

        $db = Db::getInstance();

        //getting author id and group id, checking topic id
        $result = $db->sendQuery('SELECT id_comment , user_name, text,date_added FROM forum_comments,users WHERE users.id_user = forum_comments.id_author ' .
            ' AND forum_comments.id_topic = ' . $topicId . $idFilter . ';');

        $maxId = 0;
        $comments = array();
        $n = $result->num_rows;
        for ($i = 0; $i < $n; $i++) {
            $row = $result->fetch_row();
            $comments[] = array(
                'author' => $row[1],
                'id' => $row[0],
                'text' => $row[2],
                'date' => $row[3]);
            $maxId = ($row[0] > $maxId) ? $row[0] : $maxId;
        }

        $serializedArguments = json_encode(array(
            'succeeded' => true,
            'comments' => $comments,
            'topicId' => $topicId,
            'maxId' => $maxId));

        $responseMessage = new MessageFromServer($serializedArguments, 'Forum', $message->
            messageType, $message->id);

        ApplicationTCPConnection::sendMessage($responseMessage);

        return $n;
    }

    /**
     * adds a comment to specified topic
     * @param  MessageFromApp $message message with parameters:
     *                                 groupName, commentText, commentTopicId
     * @throws \Exception     iff there is a data or database error
     * @return boolean        true iff comment added
     */
    public function addComment(MessageFromApp $message)
    {
        //CHECK: add a comment to specified forum topic
        if (!$message->serializedArguments || !is_a($message->serializedArguments,
            'stdClass')) {
            throw new \Exception('ForumController::addTopic error: message didn\'t contain proper json object');
        }
        $args = $message->serializedArguments;
        if (!property_exists($args, 'groupName') || !property_exists($args,
            'commentTopicId') || !property_exists($args, 'commentText')) {
            throw new \Exception('ForumController::addTopic error: message parameters didn\'t contain group name' .
                ' topic title , topic text or is the topic pinned');
        }

        $db = Db::getInstance();
        $groupName = $args->groupName;
        $topicId = $args->commentTopicId;
        $author = $message->from;
        $text = $db->escape($args->commentText);
        $date = strtotime('now');

        if (strlen($text) > 2000) {
            throw new \Exception('ForumController::addComment error: comment text too long, must be below 2000 chars');
        }

        //getting author id and group id, checking topic id
        $result = $db->sendQuery('SELECT id_user , groups.id_group, title FROM users,groups,forum_topics WHERE users.user_name="' .
            $author . '"' . ' AND groups.group_name="' . $groupName .
            '" AND forum_topics.id_topic=' . $topicId . ';');
        if ($result->num_rows < 1) {
            throw new \Exception('ForumController::addComment error: didn\'t find the user ' .
                $author . ' and the group ' . $groupName . ' in the database');
        }
        $row = $result->fetch_row();
        $authorId = $row[0];
        if (!is_numeric($authorId)) {
            throw new \Exception('ForumController::addComment error: didn\'t find the user ' .
                $author . ' in the database');
        }
        $groupId = $row[1];
        if (!is_numeric($groupId)) {
            throw new \Exception('ForumController::addComment error: didn\'t find ' .
                'the group ' . $groupName . ' in the database');
        }
        $topicTitle = $row[2];
        if (!is_string($topicTitle)) {
            throw new \Exception('ForumController::addComment error: didn\'t find ' .
                'the topic with topic id ' . $topicId . ' in the database');
        }

        $result->free();
        $result = '';
        //inserting forum topic

        $inserted = $db->sendInsertQuery('INSERT INTO forum_comments (text,id_author,date_added,id_topic)' .
            "VALUES(\"$text\",$authorId,$date,$topicId)", 'forum_comments');
        $serializedArguments;

        if (!$inserted) {

            $serializedArguments = json_encode(array(
                'succeeded' => false,
                'reason' => 'Couldn\'t add a comment to the database',
                'topicId' => $topicId,
                'comment' => array(
                    'author' => $author,
                    'date' => $date,
                    'text' => $text)));
        } else {
            $commentId = $db->getLastId();

            $serializedArguments = json_encode(array(
                'succeeded' => true,
                'reason' => '',
                'topicId' => $topicId,
                'comment' => array(
                    'author' => $author,
                    'date' => $date,
                    'text' => $text,
                    'commentId' => $commentId)));
        }
        $responseMessage = new MessageFromServer($serializedArguments, 'Forum', $message->
            messageType, $message->id);

        ApplicationTCPConnection::sendMessage($responseMessage);

        return $inserted;
    }

    /**
     * adds a topic to the specified group
     * @param  MessageFromApp $message a message with groupName, topicTitle,
     *                                 topicIsPinned, topicText in it's serializedArguments
     * @throws \Exception     in case of problems with data or connection
     * @return boolean        true iff topic added
     */
    public function addTopic(MessageFromApp $message)
    {
        //CHECK: add a new topic to current group
        if (!$message->serializedArguments || !is_a($message->serializedArguments,
            'stdClass')) {
            throw new \Exception('ForumController::addTopic error: message didn\'t contain proper json object');
        }
        $args = $message->serializedArguments;
        if (!property_exists($args, 'groupName') || !property_exists($args, 'topicTitle') ||
            !property_exists($args, 'topicIsPinned') || !property_exists($args, 'topicText')) {
            throw new \Exception('ForumController::addTopic error: message parameters didn\'t contain group name' .
                ' topic title , topic text or is the topic pinned');
        }

        $db = Db::getInstance();
        $groupName = $args->groupName;
        $topicTitle = $db->escape($args->topicTitle);
        $pinned = $args->topicIsPinned;
        $author = $message->from;
        $text = $db->escape($args->topicText);
        $date = strtotime('now');

        if (strlen($text) > 2000) {
            throw new \Exception('ForumController::addTopic error: topic text too long, must be below 2000 chars');
        }

        //getting author id and group id
        $result = $db->sendQuery('SELECT id_user , id_group FROM users,groups WHERE users.user_name="' .
            $author . '"' . ' AND groups.group_name="' . $groupName . '";');
        if ($result->num_rows < 1) {
            throw new \Exception('ForumController::addTopic error: didn\'t find the user ' .
                $author . ' and the group ' . $groupName . ' in the database');
        }
        $row = $result->fetch_row();
        $authorId = $row[0];
        if (!is_numeric($authorId)) {
            throw new \Exception('ForumController::addTopic error: didn\'t find the user ' .
                $author . ' in the database');
        }
        $groupId = $row[1];
        if (!is_numeric($groupId)) {
            throw new \Exception('ForumController::addTopic error: didn\'t find ' .
                'the group ' . $groupName . ' in the database');
        }

        $result->free();
        //inserting forum topic

        $inserted = $db->sendInsertQuery('INSERT INTO forum_topics (text,is_pinned,title,id_author,id_group,date_added)' .
            "VALUES(\"$text\"," . ($pinned ? '1' : '0') . ",\"$topicTitle\",$authorId,$groupId,$date)",
            'forum_topics');

        $serializedArguments;
        if (!$inserted) {

            $serializedArguments = json_encode(array(
                'succeeded' => false,
                'reason' => 'Couldn\'t add a topic to the database',
                'topic' => array(
                    'isPinned' => $pinned,
                    'author' => $author,
                    'date' => $date,
                    'text' => $text,
                    'title' => $topicTitle,
                    'group' => $groupName,
                    'id' => $db->getLastId())));
        } else {
            $topicId = $db->getLastId();

            $serializedArguments = json_encode(array(
                'succeeded' => true,
                'reason' => '',
                'topic' => array(
                    'isPinned' => $pinned ? true : false,
                    'author' => $author,
                    'date' => $date,
                    'text' => $text,
                    'title' => $topicTitle,
                    'group' => $groupName,
                    'topicId' => intval($topicId))));
        }
        $responseMessage = new MessageFromServer($serializedArguments, 'Forum', $message->
            messageType, $message->id);

        ApplicationTCPConnection::sendMessage($responseMessage);

        return $db->getInserted();
    }

    /**
     * changes a topic's title, pinned, or content
     * @param  MessageFromApp $message a message with  topicTitle,
     *                                 topicIsPinned, topicText, topicId in it's serializedArguments
     * @throws \Exception     in case of problems with data or connection
     * @return boolean        true iff topic was changed
     */
    public function changeTopic(MessageFromApp $message)
    {
        //CHECK: add a new topic to current group
        if (!$message->serializedArguments || !is_a($message->serializedArguments,
            'stdClass')) {
            throw new \Exception('ForumController::changeTopic error: message didn\'t contain proper json object');
        }
        $args = $message->serializedArguments;
        if (!property_exists($args, 'topicTitle') || !property_exists($args,
            'topicIsPinned') || !property_exists($args, 'topicText') || !property_exists($args,
            'topicId')) {
            throw new \Exception('ForumController::changeTopic error: message parameters didn\'t contain group name' .
                ' topic title , topic text , is the topic pinned , or topic id');
        }

        $db = Db::getInstance();
        $topicTitle = $db->escape($args->topicTitle);
        $pinned = $args->topicIsPinned;
        $author = $message->from;
        $text = $db->escape($args->topicText);
        $topicId = $args->topicId;
        $date = strtotime('now');

        if (strlen($text) > 2000) {
            throw new \Exception('ForumController::changeTopic error: topic text too long, must be below 2000 chars');
        }

        //getting author id
        $manager = self::isManager($message->toGroup, $author);

        $authorFilter = '';
        if (!$manager) {
            $authorId = self::getUserId($author);
            $authorFilter = ' AND id_author=' . $authorId;
        }
        //changing forum topic

        $query = 'UPDATE forum_topics SET text = "' . $text . '"' .
            ', is_pinned=' . ($pinned?'true':'false') . ', title="' . $topicTitle . '" WHERE id_topic=' . $topicId .
            $authorFilter;

        $updated = $db->sendUpdateQuery($query);

        $updated = $updated > 0;

        $serializedArguments;
        if (!$updated) {

            $serializedArguments = json_encode(array(
                'succeeded' => false,
                'reason' => 'I am sorry, couldn\'t modify the topic, probably because you are not the author of it and you are not a manager of the group',
                'query' => $query,
                'topic' => array(
                    'isPinned' => $pinned,
                    'author' => $author,
                    'date' => $date,
                    'text' => $text,
                    'title' => $topicTitle,
                    'id' => $topicId)));
        } else {

            $serializedArguments = json_encode(array(
                'succeeded' => true,
                'reason' => '',
                'topic' => array(
                    'isPinned' => $pinned,
                    'author' => $author,
                    'date' => $date,
                    'text' => $text,
                    'title' => $topicTitle,
                    'id' => $topicId)));
        }
        $responseMessage = new MessageFromServer($serializedArguments, 'Forum', $message->
            messageType, $message->id);

        ApplicationTCPConnection::sendMessage($responseMessage);

        return $updated;
    }

}
